﻿using System;
using System.Collections.Generic;
using System.Linq;
using MongoDB.Bson;
using MongoDB.Driver;
using MongoDB.Driver.Builders;
using eCommMongo.Core;
using eCommMongo.Core.Data;
using eCommMongo.Core.Domain.Customers;
using eCommMongo.Core.Domain.FriendShips;
using eCommMongo.Core.Domain.Pictures;
using eCommMongo.Core.Domain.Posts;
using eCommMongo.Core.Domain.Products;
using eCommMongo.Core.Domain.Tags;
using eCommMongo.Service.Statuses;
using eCommMongo.Service.Tags;

namespace eCommMongo.Service.Posts
{
    public class PostService:IPostService
    {
        private IMongoRepository<Comment> _commentRep;
        private IMongoRepository<Post> _postRep;
        private IMongoRepository<Tag> _tagRep;
        private IMongoRepository<FriendShip> _friendShipRep;

        private ITagService _tagService;
        private IMongoRepository<PostPointDetail> _statusPointRep;
        private IMongoRepository<Picture> _picRep;


        public PostService(IMongoRepository<Comment> commentRep, IMongoRepository<Post> statusRep, IMongoRepository<FriendShip> friendShipRep, IMongoRepository<Tag> tagRep, ITagService tagService, IMongoRepository<PostPointDetail> statusPointRep, IMongoRepository<Picture> picRep)
        {
            _commentRep = commentRep;
            _postRep = statusRep;
            _friendShipRep = friendShipRep;
            _tagRep = tagRep;
            _tagService = tagService;
            _statusPointRep = statusPointRep;
            _picRep = picRep;
        }


        public string Update(string text,
            string customerId, 
            IList<string> pictureIds,
            bool isProudct,
            string productId, 
            IList<string> tagNames  )
        {

            var post = new Post()
                {
                    CreatedAt = DateTime.UtcNow,
                    Customer = new Customer(){Id =customerId},
                    Deleted = false,
                    Text = text,
                    IsProduct = isProudct
                  
                };

            if (isProudct)
            {
                post.Product = new Product() {Id = productId};
            }

            if (pictureIds != null)
            {
                
                post.PostPictures = pictureIds.Select((picId, index) => new PostPicture()
                {
                    DisplayOrder = index + 1,
                    Picture = new Picture(){Id = picId}
                }).ToList();

            }

            if (tagNames != null)
            {
                post.Tags = new List<StatusTag>();
                var display = 0; 
                foreach (var tag in tagNames)
                {
                    var tagId = _tagService.AddTag(tag, tag, customerId);

                   
                    post.Tags.Add(new StatusTag()
                        { 
                            DisplayOrder = display,
                            Tag = new Tag(){Id = tagId}
                        });
                    display++;
                    
                }
            }

            post.Comments= new List<Comment>();
            
            var postId=  _postRep.Insert(post);
            return postId;
        }


    

        public IList<Post> UserTimeLine(string customerId, int pageIndex, int pageSize)
        {
            var query = (from s in _postRep.Table()
                        where s.Customer.Id== customerId
                              && s.Deleted == false
                              orderby s.CreatedAt descending 
                        select s);

            var tempResult = new PagedList<Post>(query, pageIndex, pageSize);
             return PostMapper.ToPostList(tempResult.ToList());
           
        }
        
        public IList<Post> HomeTimeLine(string customerId,int pageIndex, int pageSize, long? lastPostDate)
        {
            var relations = (from f in _friendShipRep.Table()
                            where f.Owner.Id == customerId
                            select f.Friend.Id).ToList();

            IQueryable<Post> posts = null;
            if (lastPostDate.HasValue)
            {
                IMongoQuery notdeled = Query.EQ("Deleted", false);
                IMongoQuery relationsquery = Query.In("Customer._id", new BsonArray(relations));
                IMongoQuery customerIdquery = Query.EQ("Customer._id", customerId);
                IMongoQuery orQuery = Query.Or(relationsquery, customerIdquery);
                IMongoQuery ticksquery= Query.LT("CreatedAt.Ticks", lastPostDate.Value);

                var mongoQuery = Query.And(orQuery, notdeled, ticksquery);

                posts = _postRep.Table(mongoQuery);
                posts = posts.OrderByDescending(c => c.CreatedAt);
                posts = posts.Take(pageSize);
                return PostMapper.ToPostList(posts.ToList());
            }
            else
            {
                posts = (from s in _postRep.Table()
                         where s.Customer.Id == customerId
                               || (relations.Contains(s.Customer.Id))
                               && s.Deleted == false
                         select s); 
                posts = posts.OrderByDescending(c => c.CreatedAt);

                var tempResult = new PagedList<Post>(posts, pageIndex, pageSize);

                return PostMapper.ToPostList(tempResult.ToList());
            }
          
            
        }

        public IList<Post> HomeTimeLineProduct(string customerId, int pageIndex, int pageSize)
        {
            var relations = (from f in _friendShipRep.Table()
                             where f.Owner.Id == customerId
                             select f.Friend.Id).ToList();

            var posts = (from s in _postRep.Table()
                          where s.Customer.Id == customerId
                                || (relations.Contains(s.Customer.Id))
                                && s.Deleted == false
                                && s.IsProduct==true
                          select s);


            posts = posts.OrderByDescending(c => c.CreatedAt);


            var tempResult = new PagedList<Post>(posts, pageIndex, pageSize);

            return PostMapper.ToPostList(tempResult.ToList());
        }

        public Post GetPostById(string postId )
        {
           

            var postQuery = from s in _postRep.Table()
                              where s.Id == postId
                              select s;

            var post = postQuery.SingleOrDefault();

            if (post != null)
            {
                return PostMapper.ToPostModel(post);
            }
            else
            {
                return null;
            }
        }

        public IPagedList<Post> FindPostsByTagId(string tagId, int pageIndex, int pageSize)
        {

            var query = new QueryDocument { { "Tags.Tag._id", tagId } };

            var tagQuery = from c in _postRep.Table(query)
                           orderby c.CreatedAt descending
                           select c;


            var tempResult = new PagedList<Post>(tagQuery, pageIndex, pageSize);

            var list = tempResult.Select(c =>PostMapper.ToPostModel(c)).ToList();

            var pagedList = new PagedList<Post>(list, pageIndex, pageSize, query.Count());
            return pagedList;
        }

        public void GivePointToPost(string postId, string customerId, int point = 1)
        {
           

            var update = Update<Post>.Inc(e => e.Point, point);
            _postRep.Update( postId, update);


            var pointDetail = new PostPointDetail()
                {
                    Customer = new Customer() {Id = customerId},
                    CreatedAt = DateTime.Now,
                    Point = point,
                    Status = new Post() {Id = postId},
                };
            _statusPointRep.Insert(pointDetail);

        }

        public int? GetPointOfStatus(string postId)
        {
            var query = from s in _postRep.Table()
                        where s.Id == postId
                        select s.Point;
            return query.SingleOrDefault();

        }


        public string AddComment(string postId, string commenterId, string text)
        {

            //var status = this._statusRep.GetById(EntityNames.Statuses, statusId);

            var commentId = ObjectId.GenerateNewId().ToString();
            var comment = new Comment
            {
                Id = commentId,
                Commenter = new Customer() { Id = commenterId },
                Text = text,
                CreatedAt = DateTime.Now
            };


            //status.Comments.Add(comment);

            //var update = Update<Status>.Set(e => e.Comments, status.Comments);

            var update = Update<Post>.Push(e => e.Comments, comment);
            _postRep.Update(postId, update);
            return commentId;
        }

        public IList<Comment> GetPagedComments(string postId, int pageIndex, int pageSize)
        {
            var query = from c in _postRep.Table()
                    where c.Id==postId 
                    select c;

            var post = query.SingleOrDefault();
            var tempResult = new PagedList<Comment>(post.Comments==null? new List<Comment>() : post.Comments , pageIndex, pageSize);

            return PostMapper.ToComments(tempResult.ToList()).ToList();
        }

        public Comment GetComment(string postId, string commentId)
        {
            var query = from p in _postRep.Table()
                        where p.Id == postId
                        select p;
            var post = query.SingleOrDefault();

            if(post ==null)
                throw new Exception(string.Format("post id{0} not exist", postId));

            var comment = post.Comments.SingleOrDefault(c => c.Id == commentId);
            return PostMapper.ToComment(comment);
        }
    }
}